package com.jzsk.variable.center.zhengxin.base.configures;

import com.jzsk.variable.center.common.utils.redis.JedisConfig;
import com.jzsk.variable.center.common.utils.redis.JedisUtil;
import com.jzsk.variable.center.domain.constants.Environment;
import com.jzsk.variable.center.zhengxin.base.FastJson2JsonRedisSerializer;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.CacheKeyPrefix;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.cache.RedisCacheWriter;
import org.springframework.data.redis.connection.RedisPassword;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import java.time.Duration;
import java.util.HashMap;
import java.util.Map;

/**
 * @author jiangli
 */
@Configuration
@EnableCaching
public class CacheConfigure extends CachingConfigurerSupport {

    @Value("${redis.host}")
    private String host;
    @Value("${redis.port}")
    private int port;
    @Value("${redis.password}")
    private String password;
    @Value("${redis.database}")
    private int database;

    @Value("${redis.maxTotal}")
    private int maxTotal;

    @Value("${spring.profiles.active}")
    private String environment;

    public CacheConfigure() {
        super();
    }

    @Override
    @Bean
    public KeyGenerator keyGenerator() {
        return (target, method, params) -> {
            StringBuilder sb = new StringBuilder();
            sb.append(target.getClass().getCanonicalName()).append("_");
            sb.append(method.getName()).append("_");
            for (Object obj : params) {
                sb.append(obj.toString()).append("_");
            }
            return sb.toString();
        };
    }

    @Bean
    public JedisUtil jedisUtil(){
        JedisConfig config = new JedisConfig();
        config.setHost(host);
        config.setPort(port);
        config.setPassword(password);
        config.setDatabase(database);
        config.setMaxTotal(maxTotal);
        return new JedisUtil(config);
    }

    @Bean
    public JedisConnectionFactory jedisConnectionFactory() {
        RedisPassword redisPassword = RedisPassword.of(password);
        RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration(host, port);
        redisStandaloneConfiguration.setPassword(redisPassword);
        redisStandaloneConfiguration.setDatabase(database);
        JedisConnectionFactory jedisConnectionFactory = new JedisConnectionFactory(redisStandaloneConfiguration);
        return jedisConnectionFactory;
    }

    @Bean
    public CacheManager cacheManager(JedisConnectionFactory jedisConnectionFactory) {

        RedisCacheWriter redisCacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(jedisConnectionFactory);

        Map<String, RedisCacheConfiguration> redisCacheConfigurationMap = new HashMap<>();
        if (environment.equals(Environment.ENVIRONMENT_PRD)) {
            redisCacheConfigurationMap.put("Minute_10", getCacheConfig("Minute_10", Duration.ofMinutes(10)));
            redisCacheConfigurationMap.put("Minute_30", getCacheConfig("Minute_30", Duration.ofMinutes(30)));
            redisCacheConfigurationMap.put("Hour_1", getCacheConfig("Hour_1", Duration.ofHours(1)));
            redisCacheConfigurationMap.put("Day_1", getCacheConfig("Day_1", Duration.ofDays(1)));
            redisCacheConfigurationMap.put("Day_7", getCacheConfig("Day_7", Duration.ofDays(7)));
        } else {
            //开发测试环境一律60秒，方便开发调试(不用频繁手动清缓存)
            redisCacheConfigurationMap.put("Minute_10", getCacheConfig("Minute_10", Duration.ofMinutes(1)));
            redisCacheConfigurationMap.put("Minute_30", getCacheConfig("Minute_30", Duration.ofMinutes(1)));
            redisCacheConfigurationMap.put("Hour_1", getCacheConfig("Hour_1", Duration.ofMinutes(1)));
            redisCacheConfigurationMap.put("Day_1", getCacheConfig("Day_1", Duration.ofDays(1)));
            redisCacheConfigurationMap.put("Day_7", getCacheConfig("Day_7", Duration.ofMinutes(1)));
        }

        return RedisCacheManager.builder(jedisConnectionFactory)
                .cacheDefaults(getDefaultConfig())
                .withInitialCacheConfigurations(redisCacheConfigurationMap)
                .build();
    }

    private RedisCacheConfiguration getDefaultConfig() {
        RedisCacheConfiguration defaultConfig = RedisCacheConfiguration.defaultCacheConfig();

        FastJson2JsonRedisSerializer<Object> jsonRedisSerializer = new FastJson2JsonRedisSerializer<>(Object.class);
        RedisSerializer<String> redisSerializer = new StringRedisSerializer();

        /*Jackson2JsonRedisSerializer jsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);

        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jsonRedisSerializer.setObjectMapper(om);*/

        defaultConfig.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jsonRedisSerializer));
        //设置默认过期时间10分钟
        defaultConfig.entryTtl(Duration.ofMinutes(10));

        return defaultConfig;
    }

    private RedisCacheConfiguration getCacheConfig(String cacheName, Duration duration) {
        return getDefaultConfig().entryTtl(duration).disableCachingNullValues()
                .computePrefixWith(CacheKeyPrefix.simple());
    }

}